/**
 * Title :	jFamilyTree generator
 * Description: jFamilyTree generator
 * Version:	provider
 * Copyright (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.generator.domtree;

import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.tree.TreeCellRenderer;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Renderer
 * 
 * @author Bernard Bou
 */
public class Renderer implements TreeCellRenderer
{
	private static final long serialVersionUID = 1L;

	// icons

	/**
	 * Element icon
	 */
	static private ImageIcon theElementIcon;

	/**
	 * Text icon
	 */
	static private ImageIcon theTextIcon;

	/**
	 * Default icon
	 */
	static private ImageIcon theDefaultIcon;

	// styles

	/**
	 * Style for element name
	 */
	static private SimpleAttributeSet theElementNameStyle = new SimpleAttributeSet();

	/**
	 * Style for element data
	 */
	static private SimpleAttributeSet theElementValueStyle = new SimpleAttributeSet();

	/**
	 * Style for node name
	 */
	static private SimpleAttributeSet theDefaultNameStyle = new SimpleAttributeSet();

	/**
	 * Style for node data
	 */
	static private SimpleAttributeSet theDefaultValueStyle = new SimpleAttributeSet();

	/**
	 * Style for id data
	 */
	static private SimpleAttributeSet theIdStyle = new SimpleAttributeSet();

	/**
	 * Patterns
	 */
	private Pattern theIdPattern = Pattern.compile("id=\"(([^\"]*))\""); //$NON-NLS-1$

	/**
	 * Pattern list
	 */
	protected List<Pattern> thePatterns;

	/**
	 * Styles for patterns
	 */
	protected Map<Pattern, SimpleAttributeSet> thePatternToStyleMap;

	static
	{
		// styles
		StyleConstants.setFontFamily(theElementNameStyle, "Dialog"); //$NON-NLS-1$
		StyleConstants.setFontSize(theElementNameStyle, 10);
		StyleConstants.setBold(theElementNameStyle, true);
		StyleConstants.setBackground(theElementNameStyle, new Color(255, 255, 200));

		StyleConstants.setFontFamily(theElementValueStyle, "Dialog"); //$NON-NLS-1$
		StyleConstants.setFontSize(theElementValueStyle, 10);

		StyleConstants.setFontFamily(theDefaultNameStyle, "Dialog"); //$NON-NLS-1$
		StyleConstants.setFontSize(theDefaultNameStyle, 10);
		StyleConstants.setItalic(theDefaultNameStyle, true);
		StyleConstants.setForeground(theDefaultNameStyle, Color.GRAY);

		StyleConstants.setFontFamily(theDefaultValueStyle, "Dialog"); //$NON-NLS-1$
		StyleConstants.setFontSize(theDefaultValueStyle, 10);
		StyleConstants.setForeground(theDefaultValueStyle, Color.GRAY);

		StyleConstants.setForeground(theIdStyle, Color.BLUE);

		// icons
		theElementIcon = new ImageIcon(Renderer.class.getResource("images/treeelement.gif")); //$NON-NLS-1$
		theTextIcon = new ImageIcon(Renderer.class.getResource("images/treetext.gif")); //$NON-NLS-1$
		theDefaultIcon = new ImageIcon(Renderer.class.getResource("images/treedefault.gif")); //$NON-NLS-1$
	}

	// components

	/**
	 * Renderer component
	 */
	protected JPanel thePanel;

	/**
	 * Icon subcomponent
	 */
	protected JLabel theIconComponent;

	/**
	 * Text sub component
	 */
	protected JTextPane theTextComponent;

	/**
	 * Constructor
	 */
	public Renderer()
	{
		// components
		theIconComponent = new JLabel();
		theTextComponent = new JTextPane();
		
		// assemble
		theIconComponent.setAlignmentY(Component.BOTTOM_ALIGNMENT);
		theTextComponent.setAlignmentY(Component.BOTTOM_ALIGNMENT);
		thePanel = new JPanel();
		thePanel.setOpaque(false);
		thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.X_AXIS));
		thePanel.add(theIconComponent);
		thePanel.add(theTextComponent);

		// styles for patterns
		thePatterns = new ArrayList<Pattern>();
		thePatterns.add(theIdPattern);
		thePatternToStyleMap = new HashMap<Pattern, SimpleAttributeSet>();
		thePatternToStyleMap.put(theIdPattern, theIdStyle);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean)
	 */
	public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
	{
		Node thisNode = (Node) value;

		// decorator
		DefaultDecorator thisDecorator = makeDecorator(thisNode);

		// strings
		String thisName = thisDecorator.getName();
		String thisValue = thisDecorator.getValue();
		int thisNameLength = thisName.length();

		// document
		StyledDocument thisStyledDocument = theTextComponent.getStyledDocument();

		// set text
		try
		{
			thisStyledDocument.remove(0, thisStyledDocument.getLength());
			thisStyledDocument.insertString(0, thisName, getNameStyle(thisNode));
			if (thisValue != null)
			{
				thisStyledDocument.insertString(thisNameLength, " ", null); //$NON-NLS-1$
				thisStyledDocument.insertString(thisNameLength + 1, thisValue, getValueStyle(thisNode));
			}
		}
		catch (BadLocationException thisException)
		{
		}

		// patterns
		applyStyleToPatterns(thisValue, thisNameLength);

		// icon
		Icon thisIcon = getIconStyle(thisNode);
		theIconComponent.setIcon(thisIcon);

		return thePanel;
	}

	/**
	 * Apply styles to patterns
	 * 
	 * @param thisString
	 *        value string
	 * @param thisOffset
	 *        offset of value in text
	 */
	protected void applyStyleToPatterns(String thisString, int thisOffset)
	{
		if (thisString != null)
		{
			StyledDocument thisStyledDocument = theTextComponent.getStyledDocument();
			for (Pattern thisPattern : thePatterns)
			{
				Matcher thisMatcher = thisPattern.matcher(thisString);
				while (thisMatcher.find())
				{
					for (int g = 1; g < thisMatcher.groupCount(); g++)
					{
						int from = thisMatcher.start(g) + thisOffset + 1;
						int to = thisMatcher.end(g) + thisOffset + 1;
						thisStyledDocument.setCharacterAttributes(from, to - from, thePatternToStyleMap.get(thisPattern), false);
					}
				}
			}
		}
	}

	/**
	 * Make node decorator
	 * 
	 * @param thisNode
	 *        node
	 * @return node decorator
	 */
	protected DefaultDecorator makeDecorator(Node thisNode)
	{
		return thisNode instanceof Element ? new ElementDecorator((Element) thisNode) : new DefaultDecorator(thisNode);
	}

	/**
	 * Get name style for node
	 * 
	 * @param thisNode
	 *        node
	 * @return style
	 */
	protected SimpleAttributeSet getNameStyle(Node thisNode)
	{
		return thisNode instanceof Element ? theElementNameStyle : theDefaultNameStyle;
	}

	/**
	 * Get value style for node
	 * 
	 * @param thisNode
	 *        node
	 * @return style
	 */
	protected SimpleAttributeSet getValueStyle(Node thisNode)
	{
		return thisNode instanceof Element ? theElementValueStyle : theDefaultValueStyle;
	}

	/**
	 * Get icon for node
	 * 
	 * @param thisNode
	 *        node
	 * @return style
	 */
	protected Icon getIconStyle(Node thisNode)
	{
		switch (thisNode.getNodeType())
		{
		case Node.ELEMENT_NODE:
			return theElementIcon;
		case Node.TEXT_NODE:
			return theTextIcon;
		default:
			return theDefaultIcon;
		}
	}
}